/* Developed with aid from
 * http://www.webglacademy.com/
 */

var canvas = document.querySelector('canvas');

canvas.height = window.innerHeight;
canvas.width = window.innerWidth;

// WebGL context.
try {
	var gl = canvas.getContext('webgl');
} catch (e) {
	console.log('A WebGL context could not be initialised:', e);
	return null;
}

function getShader(gl, id) {
	var shaderScript = document.getElementById(id);
	if (!shaderScript) {
		return null;
	}

	// Having shaders in a script in the head makes it possible to have them
	// in nice plain text as opposed to uncomfortable \n\ line breaks
	// and the like that some sources suggest. 
	var str = '';
	var k = shaderScript.firstChild;
	while (k) {
		if (k.nodeType == 3) {
			str += k.textContent;
		}
		k = k.nextSibling;
	}

	var shader;
	if (shaderScript.type === "x-shader/x-vertex") {
		shader = gl.createShader(gl.VERTEX_SHADER);
	} else if (shaderScript.type === "x-shader/x-fragment") {
		shader = gl.createShader(gl.FRAGMENT_SHADER);
	} else {
		return null;
	}

	gl.shaderSource(shader, str);
	gl.compileShader(shader);

	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		console.log(gl.getShaderInfoLog(shader));
		return null;
	}

	return shader;
}

var vertexShader = getShader(gl, 'vertexShader'),
	fragmentShader = getShader(gl, 'fragmentShader'),
	shaderProgram = gl.createProgram();

gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

var colour = gl.getAttribLocation(shaderProgram, 'color'),
	position = gl.getAttribLocation(shaderProgram, 'position');

// Attributes must be enabled.
gl.enableVertexAttribArray(position);

gl.useProgram(shaderProgram);

//Get uniform location
var Mmatrix = gl.getUniformLocation(shaderProgram, 'Mmatrix');

// Define things to draw.
var triangleVertices = [
	 0.1,  0.622008459, 0.0, // top
     -0.5, -0.351004243, 0.0, // bottom left
     0.5, -0.411004243, 0.0  // bottom right
];

var cube_vertex=[
    -1,-1,-1,
    
    0,0,0,
    
    1,-1,-1,
    
    1,0,0,
    
    1,1,-1,
    
    1,1,0,
    
    -1,1,-1,
    
    0,1,0,
    
    -1,-1,1,
    
    0,0,1,
    
    1,-1,1,
    
    1,0,1,
    
    1,1,1,
    
    1,1,1,
    
    -1,1,1,
    
    0,1,1
];


var triangleVerticesBuffer = gl.createBuffer();

gl.bindBuffer(gl.ARRAY_BUFFER, triangleVerticesBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);


var triangleFaces = [
	 0,  1,  2
];

var triangleFacesBuffer = gl.createBuffer();
	
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleFacesBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(triangleFaces), gl.STATIC_DRAW);

// Matrices.
var movementMatrix = matrix.getIdentityMatrix();

gl.viewport(0.0, 0.0, canvas.width, canvas.height);
gl.clearColor(0.0, 0.0, 0.0, 1.0);

//var _color = gl.getAttribLocation(shaderProgram, "color");
//gl.enableVertexAttribArray(_color);

function draw() {
    //rotate();

	gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
	gl.uniformMatrix4fv(Mmatrix, false, movementMatrix);
	
	gl.vertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0);
	
	gl.bindBuffer(gl.ARRAY_BUFFER, triangleVerticesBuffer);
	//Render triangle like this
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    //or like this
    //gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, triangleFacesBuffer);
	//gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_SHORT, 0);
   
	window.requestAnimationFrame(draw);
}

var rotX = 0;
var rotY = 0;
var rotZ = 0;
var rotSpeed = 10.0;
var oldDate = new Date().getMilliseconds();

function rotate() {
    var newDate = new Date().getMilliseconds();
    var elapsedTime = newDate - oldDate;

    rotX += rotSpeed * (elapsedTime * 0.001);
    rotY += rotSpeed * (elapsedTime * 0.001);
    rotZ += rotSpeed * (elapsedTime * 0.001);
    
    console.log(rotX);

    movementMatrix = matrix.getIdentityMatrix();
    movementMatrix = matrix.rotateX(movementMatrix, rotX);
    movementMatrix = matrix.rotateY(movementMatrix, rotY);
    movementMatrix = matrix.rotateZ(movementMatrix, rotZ);
    
    oldDate = newDate;
}

document.addEventListener('keydown', function(event) {
    //Left
    if(event.keyCode == 37) {
        rotateCameraLeft();
    }
    //Right
    if(event.keyCode == 39) {
        rotateCameraRight();
    }
    //Up
    if(event.keyCode == 38) {
        rotateCameraUp();
    }
    //Down
    if(event.keyCode == 40) {        
        rotateCameraDown();
    }
});

function rotateCameraUp() {
    movementMatrix = matrix.rotateX(movementMatrix, 0.1);
}

function rotateCameraDown() {
    movementMatrix = matrix.rotateX(movementMatrix, -0.1);
}

function rotateCameraLeft() {
    movementMatrix = matrix.rotateY(movementMatrix, -0.1);
}

function rotateCameraRight() {
    movementMatrix = matrix.rotateY(movementMatrix, 0.1);
}

function LoadModel(ModelName, CB) {
    var Ajax = new XMLHttpRequest();
    Ajax.onreadystatechange = function () {
        if (Ajax.readyState == 4 && Ajax.status == 200) {
            //Parse Model Data
            var Script = Ajax.responseText.split("\n");
 
            var Vertices = [];
            var VerticeMap = [];
 
            var Triangles = [];
 
            var Textures = [];
            var TextureMap = [];
 
            var Normals = [];
            var NormalMap = [];
 
            var Counter = 0;

            for (var I in Script) {
                var Line = Script[I];
                //If Vertice Line
                if (Line.substring(0, 2) == "v ") {
                    var Row = Line.substring(2).split(" ");
                    Vertices.push({
                        X: parseFloat(Row[0]),
                        Y: parseFloat(Row[1]),
                        Z: parseFloat(Row[2])
                    });
                }
                    //Texture Line
                else if (Line.substring(0, 2) == "vt") {
                    var Row = Line.substring(3).split(" ");
                    Textures.push({
                        X: parseFloat(Row[0]),
                        Y: parseFloat(Row[1])
                    });
                }
                    //Normals Line
                else if (Line.substring(0, 2) == "vn") {
                    var Row = Line.substring(3).split(" ");
                    Normals.push({
                        X: parseFloat(Row[0]),
                        Y: parseFloat(Row[1]),
                        Z: parseFloat(Row[2])
                    });
                }

                    //Mapping Line
                else if (Line.substring(0, 2) == "f ") {
                    var Row = Line.substring(2).split(" ");
                    for (var T in Row) {
                        //Remove Blank Entries
                        if (Row[T] != "") {
                            //If this is a multi-value entry
                            if (Row[T].indexOf("/") != -1) {
                                //Split the different values
                                var TC = Row[T].split("/");
                                //Increment The Triangles Array
                                Triangles.push(Counter);
                                Counter++;
 
                                //Insert the Vertices 
                                var index = parseInt(TC[0]) - 1;
                                VerticeMap.push(Vertices[index].X);
                                VerticeMap.push(Vertices[index].Y);
                                VerticeMap.push(Vertices[index].Z);
 
                                //Insert the Textures
                                index = parseInt(TC[1]) - 1;
                                TextureMap.push(Textures[index].X);
                                TextureMap.push(Textures[index].Y);
 
                                //If This Entry Has Normals Data
                                if (TC.length > 2) {
                                    //Insert Normals
                                    index = parseInt(TC[2]) - 1;
                                    NormalMap.push(Normals[index].X);
                                    NormalMap.push(Normals[index].Y);
                                    NormalMap.push(Normals[index].Z);
                                }
                            }
                                //For rows with just vertices
                            else {
                                Triangles.push(Counter); //Increment The Triangles Array
                                Counter++;
                                var index = parseInt(Row[T]) - 1;
                                VerticeMap.push(Vertices[index].X);
                                VerticeMap.push(Vertices[index].Y);
                                VerticeMap.push(Vertices[index].Z);
                            }
                        }
                    }
                }
            }
            //Return The Arrays
            CB(VerticeMap, Triangles, TextureMap, NormalMap);
        }
    }
    Ajax.open("GET", ModelName + ".obj", true);   
    Ajax.send();
}

draw();
//this.Draw();

function PrepareModel (Model) {
    Model.Image = this.LoadTexture(Model.Image);
 
    //Convert Arrays to buffers
    var Buffer = gl.createBuffer();
 
    gl.bindBuffer(gl.ARRAY_BUFFER, Buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(Model.Vertices), gl.STATIC_DRAW);
    Model.Vertices = Buffer;
 
    Buffer = gl.createBuffer();
 
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Buffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(Model.Triangles), gl.STATIC_DRAW);
    Model.Triangles = Buffer;
 
    Buffer = gl.createBuffer();
 
    gl.bindBuffer(gl.ARRAY_BUFFER, Buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(Model.TextureMap), gl.STATIC_DRAW);
    Model.TextureMap = Buffer;
 
    Model.Ready = true;
};

function Draw (Model) {
    if (Model.Image.ReadyState == true && Model.Ready == false) {
        PrepareModel(Model);
    }
    if (Model.Ready) {
        gl.bindBuffer(gl.ARRAY_BUFFER, Model.Vertices);
        gl.vertexAttribPointer(this.VertexPosition, 3, gl.FLOAT, false, 0, 0);
 
 
        gl.bindBuffer(gl.ARRAY_BUFFER, Model.TextureMap);
        gl.vertexAttribPointer(this.VertexTexture, 2, gl.FLOAT, false, 0, 0);
 
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Model.Triangles);
 
        //Generate The Perspective Matrix
        var PerspectiveMatrix = MakePerspective(45, this.AspectRatio, 1, 1000.0);
 
        var TransformMatrix = Model.GetTransforms();
        //Set slot 0 as the active Texture
        gl.activeTexture(gl.TEXTURE0);
 
        //Load in the Texture To Memory
        gl.bindTexture(gl.TEXTURE_2D, Model.Image);
 
        //Update The Texture Sampler in the fragment shader to use slot 0
        gl.uniform1i(gl.getUniformLocation(this.ShaderProgram, "uSampler"), 0);
 
        //Set The Perspective and Transformation Matrices
        var pmatrix = gl.getUniformLocation(this.ShaderProgram, "PerspectiveMatrix");
        gl.uniformMatrix4fv(pmatrix, false, new Float32Array(PerspectiveMatrix));
 
        var tmatrix = gl.getUniformLocation(this.ShaderProgram, "TransformationMatrix");
        gl.uniformMatrix4fv(tmatrix, false, new Float32Array(TransformMatrix));
 
        //Draw The Triangles
        gl.drawElements(gl.TRIANGLES, Model.TriangleCount, gl.UNSIGNED_SHORT, 0);
    }
};

var Building;

LoadModel("House", function(VerticeMap, Triangles, TextureMap){
    Building = new GLObject(VerticeMap, Triangles, TextureMap, "House.png");
    Building.Pos.Z = 650;
    Building.Scale.X = 0.5;
    Building.Scale.Y = 0.5;
    Building.Scale.Z = 0.5;
    Building.Rotation.Y = 180;
    setInterval(Update, 33);
    document.onkeydown = handleKeyDown;
});
